package com.example.demo.mqserver.dao;

import com.example.demo.common.BinaryTool;
import com.example.demo.common.MQException;
import com.example.demo.mqserver.core.MSGQueue;
import com.example.demo.mqserver.core.Message;

import java.io.*;
import java.security.cert.TrustAnchor;
import java.util.Scanner;

public class MessageFileManager {
    // 表示消息的统计信息
    static public class Stat {
        public int totalCount ;
        public int validCount;
    }

    public void init () {
    }

    // 获取队列目录
    private String getQueueDir (String queueName) {
        return "./data/" + queueName;
    }

    // 获取数据文件路径
    // 二进制文件后缀一般是 .dat 或者 .bin , 此处直接适用.txt
    private String getQueueDataPath (String queueName) {
        return getQueueDir(queueName) + "/queue_data.txt";
    }

    // 获取状态文件路径
    public String getQueueStatPath (String queueName) {
        return getQueueDir(queueName) + "/queue_stat.txt";
    }


    private Stat readStat (String queueName) {
        // 文件文件 , 直接scanner读取即可
        Stat stat = new Stat();
        try (InputStream io= new FileInputStream(getQueueStatPath(queueName))) {
            Scanner scanner = new Scanner(io);
            stat.totalCount = scanner.nextInt(); // 总的消息数量
            stat.validCount = scanner.nextInt(); // 有效消息数量
            return stat;
        }catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void writeStat (String queueName , Stat stat) {
        try (OutputStream outputStream = new FileOutputStream(getQueueStatPath(queueName))) {
            PrintWriter printWriter = new PrintWriter(outputStream);
            printWriter.write(stat.totalCount + "\t" + stat.validCount);
            printWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 创建队列对应的文件和目录
    public void createQueueFile (String queueName) throws IOException {
        File dir = new File(getQueueDir(queueName));
        if (!dir.exists()) {
            boolean ok = dir.mkdirs();
            if (!ok) {
                throw new IOException("创建目录失败! dir : " + dir.getAbsolutePath());
            }
        }
        // 创建文件
        File dataFile = new File(getQueueDataPath(queueName));
        if (!dataFile.exists()) {
            // 文件不存在 , 创建文件
            boolean ok = dataFile.createNewFile();
            if (!ok) {
                throw new IOException("创建队列数据文件失败! " + dataFile.getAbsolutePath());
            }
        }

        // 创建消息统计文件
        File statFile = new File(getQueueStatPath(queueName));
        if (!statFile.exists()) {
            if (!statFile.exists()) {
                boolean ok = statFile.createNewFile();
                if (!ok) {
                    throw new IOException("创建队列状态文件失败!" + statFile.getAbsolutePath());
                }
            }
        }

        // 状态文件初始化
        Stat stat = new Stat();
        stat.totalCount = 0;
        stat.validCount = 0;
        writeStat(queueName,stat);
    }

    // 删除指定队列的目录文件
    public void destroyQueueFile (String queueName) throws IOException {
        File dataFile = new File(getQueueDataPath(queueName));
        boolean ok1 = dataFile.delete();
        File statFile = new File(getQueueStatPath(queueName));
        boolean ok2 = statFile.delete();
        File dir = new File(getQueueDir(queueName));
        boolean ok3 = dir.delete();

        if (!ok1 || !ok2 || !ok3) {
            throw new IOException("删除队列目录和文件失败" + dir.getAbsolutePath());
        }

    }

    /**
     * 检查队列的数据文件和状态文件是否存在
     * @param queueName 队列名字
     * @return true 文件存在 <br/> false 文件不完整
     */
    public boolean checkFileExists (String queueName) {
        File dataFile = new File(getQueueDataPath(queueName));
        if (!dataFile.exists()) {
            return false;
        }
        File statFile = new File(getQueueStatPath(queueName));
        if (!statFile.exists()) {
            return false;
        }
        return true;
    }


    /**
     * 将消息写入指定的队列中
     * @param queue  要写入的队列
     * @param message 要写入的消息
     */
    public void sendMessage (MSGQueue queue , Message message) throws MQException, IOException {
        // 检查队列对应的文件是否存在
        boolean exissts = checkFileExists(queue.getName());
        if (!exissts) {
            throw new MQException("队列文件不存在" + queue.getName());
        }
        // 将消息序列化
        byte[] binary_message = BinaryTool.toBytes(message);
        synchronized (queue) {
            // 获取当前队列数据文件的长度 , 计算改队列的起始值和末尾值
            File dataFile = new File(getQueueDataPath(queue.getName()));
            message.setOffsetBeg(dataFile.length()+4);
            message.setOffsetEnd(dataFile.length() + 4 + binary_message.length);

            // 追加写入
            try(OutputStream outputStream = new FileOutputStream(dataFile,true)) {
                try(DataOutputStream dataOutputStream = new DataOutputStream(outputStream))  {
                    dataOutputStream.writeInt(binary_message.length);
                    dataOutputStream.write(binary_message);
                }
            }
            // 修改状态文件
            Stat stat = readStat(queue.getName());
            stat.totalCount = stat.totalCount + 1;
            stat.validCount = stat.validCount + 1;
            writeStat(queue.getName() , stat);
        }

    }

    /**
     * 删除指定队列的文件  逻辑和三处,并非真正的删除
     * @param queue   要删除文件所在的队列
     * @param message 删除的信息
     */
    public void deleteMessage (MSGQueue queue, Message message) {
        long begin = message.getOffsetBeg();
        long end = message.getOffsetEnd();
        // 只需要将消息重新写入即可


        Stat stat = readStat(queue.getName());
        if (stat.validCount > 0) {
            stat.validCount = stat.validCount - 1;
        }
        writeStat(queue.getName() , stat);

    }






}
